using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace XDebugConsole.Components
{
    /// <summary>
    /// Places marks on 3D space with labels on 2D space
    /// </summary>
    public class Marker : DebugComponent
    {
        private Hashtable markTable = new Hashtable();
        private SpriteBatch spriteBatch;
        private BasicEffect effect;
        private DebugSharedResources resources;

        #region Constructor
        public Marker(DebugSharedResources resources)
            : base(resources)
        {
            this.spriteBatch = resources.spriteBatch;
            //this.effect = resources.effect;
            this.resources = resources;
            this.effect = resources.effect;
            HandleCommand("mark");
        }
        #endregion


        #region Execute
        public override void Execute(string[] command, params object[] arguments)
        {
            StringBuilder key = new StringBuilder();
            // We need at least the command, the key and a position, and possibly a Color.
            if (command.Length < 2 || arguments.Length < 1)
            {
                return;
            }

            for (int i = 1; i < command.Length; i++)
            {
                key.Append(command[i]);
            }

            if (arguments.Length == 1)
                ShowMark(key.ToString(), (Vector3)arguments[0]);
            else
                ShowMark(key.ToString(), (Vector3)arguments[0], (Color)arguments[1]);
        }
        #endregion


        #region ShowMark (public methods)
        public void ShowMark(String key, Vector3 position, Color color)
        {
            if (!markTable.Contains(key))
                this.AddMark(key, position, color);
            else
                this.SetMark(key, position, color);

        }

        public void ShowMark(String key, Vector3 position)
        {
            if (!markTable.Contains(key))
                this.AddMark(key, position, Color.Yellow);
            else
                this.SetMark(key, position);

        }
        #endregion

        #region AddMark/SetMark (private methods)
        private void AddMark(String key, Vector3 position, Color color)
        {
            Vector2 textSize = Resources.fontTiny.MeasureString(key);
            RenderTarget2D renderTarget;

            // If the device can't use render targets other than powers of two
            // create a 256x256 texture. Otherwise just use the space neeeded.
            if (Resources.device.GraphicsDeviceCapabilities.TextureCapabilities.RequiresPower2)
            {
                renderTarget = new RenderTarget2D(Resources.device, 256, 256, 0, SurfaceFormat.Color,
                    resources.device.PresentationParameters.MultiSampleType,
                    resources.device.PresentationParameters.MultiSampleQuality);
            }
            else
            {
                renderTarget = new RenderTarget2D(Resources.device,
                        (int)Math.Ceiling(textSize.X), (int)Math.Ceiling(textSize.Y), 0, SurfaceFormat.Color,
                        resources.device.PresentationParameters.MultiSampleType,
                        resources.device.PresentationParameters.MultiSampleQuality);
            }
            RenderTarget2D savedRenderTarget = (RenderTarget2D)Resources.device.GetRenderTarget(0);

            // Draw the label to a special renderTarget and then extract the texture.
            Resources.device.SetRenderTarget(0, renderTarget);
            spriteBatch.Begin();
            spriteBatch.DrawString(Resources.fontTiny, key, Vector2.One, Color.Black);
            spriteBatch.DrawString(Resources.fontTiny, key, Vector2.Zero, Color.White);
            spriteBatch.End();
            Resources.device.SetRenderTarget(0, savedRenderTarget);

            // Finally add the mark.
            markTable.Add(key, new DebugMark(position, color, renderTarget.GetTexture()));
        }

        private void SetMark(String key, Vector3 position, Color color)
        {
            ((DebugMark)markTable[key]).MoveTo(position);
            ((DebugMark)markTable[key]).color = color;
        }

        private void SetMark(String key, Vector3 position)
        {
            ((DebugMark)markTable[key]).MoveTo(position);
        }
        #endregion

        #region Draw
        public override void Draw(Microsoft.Xna.Framework.GameTime gameTime)
        {
            DrawMarks();
            DrawMarksLabels();
            base.Draw(gameTime);
        }

        private void DrawMarks()
        {
            // Draw the marks.
            //effect.CurrentTechnique = effect.Techniques["Colored"];
            effect.View = resources.gameInfo.ViewMatrix;
            effect.Projection = resources.gameInfo.ProjectionMatrix;
            effect.World = Matrix.Identity;
            effect.VertexColorEnabled = true;
            effect.Begin(SaveStateMode.None);
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                foreach (DictionaryEntry m in markTable)
                {
                    switch (((DebugMark)m.Value).Type)
                    {
                        case MarkType.Cross:
                            Resources.device.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineList, ((DebugMark)m.Value).mark, 0, 3);
                            break;
                        case MarkType.Point:
                            Resources.device.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.PointList, ((DebugMark)m.Value).mark, 0, 1);
                            break;
                    }
                }
                pass.End();
            }
            effect.End();
        }

        private void DrawMarksLabels()
        {
            // The mark's labels
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None);
            foreach (DictionaryEntry m in markTable)
            {
                Texture2D texture = ((DebugMark)m.Value).label;
                Vector3 position = Vector3.Transform(((DebugMark)m.Value).mark[0].Position, Matrix.Identity * (GameInfo.ViewMatrix * GameInfo.ProjectionMatrix));
                if (position.Z < 0) continue;
                Rectangle dest = new Rectangle((int)(((position.X / position.Z + 1) / 2) * Resources.device.Viewport.Width),
                                                (int)(((-position.Y / position.Z + 1) / 2) * Resources.device.Viewport.Height),
                                                (int)(texture.Width),
                                                (int)(texture.Height));
                //Color color = new Color((byte)(255 - position.Z * 10), (byte)(255 - position.Z * 10), (byte)(255 - position.Z * 10));
                spriteBatch.Draw(texture, dest, Color.White);
            }
            spriteBatch.End();
        }
        #endregion
    }
}
